/*
 *
 * Copyright (C) 2012 Walter Lütgenau
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *      http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */
package offset.nodes.server.view.css;

import java.io.FileInputStream;
import java.util.HashMap;

/**
 * Used to preprocess a CSS file to adapt content to the user agent configuration
 * 
 * @author Walter L�tgenau
 */
public class CssPreProcessor extends PreProcessor {
    static CssPreProcessor instance;
    Command[] preProcessorCommands = {
            new Command(CommandType.cssInitial, "initial", new InitialProcessor()),
            new Command(CommandType.cssIf, "if", new IfProcessor()),
            new Command(CommandType.cssElseIf, "elseif", new ElseIfProcessor()),
            new Command(CommandType.cssElse, "else", new ElseProcessor()),
            new Command(CommandType.cssEndIf, "endif", new EndIfProcessor()),
            new Command(CommandType.cssDef, "def", new DefinitionProcessor()),
    };

    public static synchronized CssPreProcessor getInstance() {
        if (instance == null)
            instance = new CssPreProcessor();
        return instance;
    }

    private CssPreProcessor() {
        init(preProcessorCommands);
    }

    public Command[] getPreProcessorCommands() {
        return preProcessorCommands;
    }

    /**
     * Evaluate a preprocessor condition
     * 
     * @param token
     * @param context
     * @return
     */
    protected boolean evaluateCondition(Token token, PreProcessorContext context) {
        boolean result = false;
        String[] words = token.getValue().split(" ");
        String name = words[0];

        boolean not = false;
        if (name.startsWith("!")) {
            not = true;
            result = true;
            name = name.substring(1);
        }

        for (int i = 1; i < words.length; i++) {
            if (context.getUserSettings().get(name) != null
                    && context.getUserSettings().get(name).equals(words[i])) {
                result = !not;
                break;
            }
        }

        return result;
    }

    /**
     * Implements @if command
     */
    class IfProcessor implements CommandProcessor {
        public String process(Token token, PreProcessorContext context) {
            String result = "";

            if (context.getConditions().size() > 0 && (context.getConditions().peek().equals(ConditionState.done)))
                return result;

            if (!evaluateCondition(token, context)) {
                context.getConditions().push(ConditionState.initial);
                return result;
            }

            context.getConditions().push(ConditionState.active);
            return token.getContent();
        }
    }

    /**
     * Implements @elseif command
     */
    class ElseIfProcessor implements CommandProcessor {
        public String process(Token token, PreProcessorContext context) {
            String result = "";

            if (context.getConditions().size() > 0) {
                if (context.getConditions().peek().equals(ConditionState.active)) {
                    context.getConditions().pop();
                    context.getConditions().push(ConditionState.done);
                    return result;
                } else if (context.getConditions().peek().equals(ConditionState.done))
                    return result;
            }

            if (!evaluateCondition(token, context))
                return result;

            context.getConditions().pop();
            context.getConditions().push(ConditionState.active);
            return token.getContent();
        }
    }

    /**
     * Implements @else command
     */
    class ElseProcessor implements CommandProcessor {
        public String process(Token token, PreProcessorContext context) {
            String result = "";

            if (context.getConditions().size() > 0) {
                if (context.getConditions().peek().equals(ConditionState.active)) {
                    context.getConditions().pop();
                    context.getConditions().push(ConditionState.done);
                    return result;
                } else if (context.getConditions().peek().equals(ConditionState.done))
                    return result;
            }

            context.getConditions().pop();
            context.getConditions().push(ConditionState.active);
            return token.getContent();
        }
    }

    /**
     * Implements initializer command
     */
    class InitialProcessor implements CommandProcessor {
        public String process(Token token, PreProcessorContext context) {
            return token.getContent();
        }
    }

    /**
     * Implements @endif command
     */
    class EndIfProcessor implements CommandProcessor {
        public String process(Token token, PreProcessorContext context) {
            String result = token.getContent();

            if (context.getConditions().size() > 0) {
                context.getConditions().pop();
                return result;
            }

            return result;
        }
    }

    /**
     * Implements @def command, i.e. stores a definition, a text under a name.
     * Such name will be substituted by the associated text in the CSS content
     */
    class DefinitionProcessor implements CommandProcessor {
        public String process(Token token, PreProcessorContext context) {
            String result = "";

            if (context.getConditions().size() > 0 && !context.getConditions().peek().equals(ConditionState.active))
                return result;

            int startValue = token.getValue().indexOf(" ");
            if (startValue < 0)
                return result;

            context.getDefinitions().put(token.getValue().substring(0, startValue), token.getValue().substring(startValue + 1));
            return result;
        }
    }

    public static void main(String arguments[]) throws Exception {
        HashMap<String, String> userSettings = new HashMap<String, String>();
        userSettings.put("uaType", "applet");
        userSettings.put("os", "windows");
        userSettings.put("language", "en");

        FileInputStream in = new FileInputStream("D:/Sources/maven/nodes/web/src/test/resources/offset/nodes/server/test/menu.css");
        byte[] buf = new byte[in.available()];
        in.read(buf);

        String input = new String(buf);

        System.out.print(CssPreProcessor.getInstance().preProcess(userSettings, input));
    }
}
